#include "functionParserWrapper.hpp"
#include "field.hpp"
#include "simulationTime.hpp"
#include "typeHelpers.hpp"

namespace hms
{

template<typename MeshType>
Index nFieldCols( const MeshBase<MeshType>& mesh ){
	if constexpr ( std::is_base_of_v<StructMeshBase<MeshType>, MeshType> ){
		static constexpr Index ng {1};
		return
			( mesh.derived().nCellsX()+2*ng ) *
			( mesh.derived().nCellsY()+2*ng );
	} else
		return mesh.nCells();
}

template<Uniformity isUniform, typename MeshType>
void resizeFieldValues(
	ArrayXXs& values,
	Index nComponents,
	[[maybe_unused]] const MeshBase<MeshType>& mesh
){
	if constexpr ( isUniform )
		values.resize(nComponents, 1);
	else
		values.resize( nComponents, nFieldCols(mesh) );
}


template<Uniformity isUniform, typename MeshType>
void assertFieldValuesSize(
	[[maybe_unused]] const ArrayXXs& values,
	[[maybe_unused]] const MeshBase<MeshType>& mesh
){
	if constexpr (isUniform){
		assert( values.cols() == 1 &&
		"Uniform field values require exactly one column vector!"
		);
	} else {
		assert( values.cols() == nFieldCols(mesh) &&
		"Number of columns for non-uniform FieldBase type incorrect! "
		"Use resizeValues()!"
		);
	}
}


template<typename T, typename MeshType>
DomainValuesType<T, MeshType> domainValues(
	T&& values, const MeshBase<MeshType>& mesh
){
	static_assert( isDenseType<remove_qualifiers<T>>() );
	if constexpr ( std::is_base_of_v<StructMeshBase<MeshType>, MeshType> ){
		static constexpr Index ng {1};
		Index
			nc  { values.rows() },
			nx  { mesh.derived().nCellsX() },
			ny  { mesh.derived().nCellsY() },
			nxg { 2*ng + nx },
			nyg { 2*ng + ny };
		return values.reshaped( nxg*nc, nyg ).block(
			ng*nc,
			ng,
			nx*nc,
			ny
		).reshaped( nc, mesh.nDomainCells() );
	}
	else
		return values.leftCols( mesh.nDomainCells() );
}

template<typename Derived, typename MeshType>
bool hasMatchingSize(
	const Eigen::DenseBase<Derived>& values,
	const MeshBase<MeshType>& mesh,
	Index rows
){
	return values.cols() == nFieldCols(mesh) &&
	( rows == signalCode::uninitialised ? true : values.rows() == rows );
}

template<Uniformity isUniform, typename SolverType>
void setFieldByFunctions(
	ArrayXXs& vals,
	const std::vector<FunctionParserWrapper>& functions,
	const SolverBase<SolverType>& solver,
	bool useZField
){
	setFieldByFunctions<isUniform>(
		vals, functions,
		solver.mesh(),
		solver.zField(),
		useZField
	);
}

template<typename SolverType>
void setFieldByFunctions(
	ArrayXXs& vals,
	const std::vector<FunctionParserWrapper>& functions,
	const SolverBase<SolverType>& solver,
	Uniformity isUniform,
	bool useZField
){
	if (isUniform)
		setFieldByFunctions<Uniform>(vals, functions, solver, useZField);
	else
		setFieldByFunctions<NonUniform>(vals, functions, solver, useZField);
}

} // namespace hms
